﻿namespace SimpleFileVerifier
{
    using System;
    using System.IO;

    public class Program
    {
        public static int Main(string[] args)
        {
            int result = 0;

            var commandLineOptions = new CommandLineOptions();

            if (CommandLine.Parser.Default.ParseArguments(args, commandLineOptions))
            {
                string outputFile = commandLineOptions.Output;
                string directory = commandLineOptions.Directory;
                string file = commandLineOptions.File;
                string pattern = commandLineOptions.Pattern;
                string check = commandLineOptions.Check;
                string recursive = commandLineOptions.Recursive;
                string logFile = commandLineOptions.Log;
                string hashInformation = string.Empty;

                Console.WriteLine("SimpleFileVerifier by Ranjith Venkatesh" + Environment.NewLine);
                Console.WriteLine("CRC32 Hashes calculated using CodeFluent Runtime Client" + Environment.NewLine);

                if (File.Exists(check))
                {
                    CheckSFVFile(check, ref hashInformation);
                    PrintToFile(logFile, hashInformation);
                }
                else if (File.Exists(file))
                {
                    PrintFileCRC32Hash(Path.GetDirectoryName(file), file, ref hashInformation);

                    Console.WriteLine(Environment.NewLine + "SimpleFileVerifier has successfully created the CRC32 hashes.");
                }
                else if (Directory.Exists(directory))
                {
                    DirectoryInfo directoryInfo = new DirectoryInfo(directory);

                    string searchPattern = "*.*";
                    if (string.IsNullOrEmpty(pattern) == false)
                    {
                        searchPattern = pattern;
                    }

                    SearchOption searchOption = SearchOption.TopDirectoryOnly;
                    if (string.IsNullOrEmpty(recursive) == false)
                    {
                        bool searchOptionRecursive = false;
                        bool.TryParse(recursive, out searchOptionRecursive);
                        if (searchOptionRecursive == true)
                        {
                            searchOption = SearchOption.AllDirectories;
                        }
                    }

                    foreach (FileInfo fileInfo in directoryInfo.GetFiles(searchPattern, searchOption))
                    {
                        if(fileInfo.FullName.EndsWith("sfv"))
                        {
                            continue;
                        }

                        PrintFileCRC32Hash(directory, fileInfo.FullName, ref hashInformation);
                    }

                    Console.WriteLine(Environment.NewLine + "SimpleFileVerifier has successfully created the CRC32 hashes.");
                    PrintToFile(outputFile, hashInformation);
                }
                else
                {
                    Console.WriteLine(commandLineOptions.GetUsage());
                    return result;
                }
                
                result = 1;
            }

            return result;
        }

        private static void PrintToFile(string outputFile, string hashInformation)
        {
            if (string.IsNullOrEmpty(outputFile) == false)
            {
                string outputDirectory = Path.GetDirectoryName(outputFile);
                if (Directory.Exists(outputDirectory))
                {
                    string header = ";" + Environment.NewLine + "; Generated by Simple File Verifier" + Environment.NewLine + "; " + DateTime.Now.ToLongDateString() + " " + DateTime.Now.ToLongTimeString() + Environment.NewLine + ";" + Environment.NewLine;
                    File.WriteAllText(outputFile, header + hashInformation);
                }
                else
                {
                    Console.WriteLine(Environment.NewLine + "SimpleFileVerifier cannot find the directory to write the file:" + outputDirectory);
                }
                Console.WriteLine(Environment.NewLine + "SimpleFileVerifier has written the to the file:" + outputFile);
            }
        }

        private static void CheckSFVFile(string check, ref string hashInformation)
        {
            string[] sfvFileContents = File.ReadAllLines(check);
            string sfvFileDirectory = Path.GetDirectoryName(check);
            foreach (string hashedFile in sfvFileContents)
            {
                if (hashedFile.StartsWith(";") == true || hashedFile.Length == 0)
                {
                    continue;
                }

                // Get file name and original CRC32 hash
                string[] hashedFileLine = hashedFile.Split(' ');

                int indexOfLastSpace = hashedFile.LastIndexOf(' ');
                string hashedFileName = hashedFile.Substring(0, indexOfLastSpace);
                string originalCrc32Hash = hashedFile.Substring(indexOfLastSpace + 1);
                
                string hashedFileNameFullPath = sfvFileDirectory + "/" + hashedFileName;
                // Check if file exists
                if (File.Exists(hashedFileNameFullPath))
                {
                    // If file exists, generate CRC32 hash and compare with original hash
                    string crc32Hash = GetCRC32Hash(hashedFileNameFullPath);

                    // Report as hash as correct or wrong
                    if (originalCrc32Hash == crc32Hash)
                    {
                        Console.WriteLine(Environment.NewLine + hashedFileName + " - OK");
                        hashInformation = hashInformation + Environment.NewLine + hashedFileName + " - OK";
                    }
                    else
                    {
                        Console.WriteLine(Environment.NewLine + hashedFileName + " - NOT OK");
                        hashInformation = hashInformation + Environment.NewLine + hashedFileName + " - NOT OK";
                    }
                }
                else
                {
                    // If file does not exist, report as file cannot be found
                    Console.WriteLine(Environment.NewLine + hashedFileName + " - File not found");
                    hashInformation = hashInformation + Environment.NewLine + hashedFileName + " - FILE NOT FOUND";
                }
                
            }            
        }

        private static void PrintFileCRC32Hash(string directory, string file, ref string hashInformation)
        {
            string crc32Hash = GetCRC32Hash(file);
            string relativeFilePath = "." + file.Replace(directory, "");
            hashInformation = hashInformation + Environment.NewLine + relativeFilePath + " " + crc32Hash;
            Console.WriteLine(relativeFilePath + " " + crc32Hash);
        }

        private static string GetCRC32Hash(string file)
        {
            byte[] fileByteArray = FileToByteArray(file);
            uint crcUintValue = CodeFluent.Runtime.Utilities.Crc32.Compute(fileByteArray);
            return string.Format("{0:X}", crcUintValue);
        }

        /// <summary>
        /// Function to get byte array from a file
        /// </summary>
        /// <param name="_FileName">File name to get byte array</param>
        /// <returns>Byte Array</returns>
        private static byte[] FileToByteArray(string _FileName)
        {
            byte[] _Buffer = null;

            try
            {
                // Open file for reading
                System.IO.FileStream _FileStream = new System.IO.FileStream(_FileName, System.IO.FileMode.Open, System.IO.FileAccess.Read);

                // attach filestream to binary reader
                System.IO.BinaryReader _BinaryReader = new System.IO.BinaryReader(_FileStream);

                // get total byte length of the file
                long _TotalBytes = new System.IO.FileInfo(_FileName).Length;

                // read entire file into buffer
                _Buffer = _BinaryReader.ReadBytes((Int32)_TotalBytes);

                // close file reader
                _FileStream.Close();
            }
            catch (Exception _Exception)
            {
                // Error
                Console.WriteLine("Exception caught in process: {0}", _Exception.ToString());
            }

            return _Buffer;
        }


    }
}
